package com.qfmy.web.app.service.impl.user;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.qfmy.common.constant.RedisConstant;
import com.qfmy.common.exception.BaseException;
import com.qfmy.common.login.CodeResult;
import com.qfmy.common.login.GraphicCodeResult;
import com.qfmy.common.login.LoginUser;
import com.qfmy.common.login.LoginUserHolder;
import com.qfmy.common.result.ResultCodeEnum;
import com.qfmy.common.utils.GraphicCodeUtil;
import com.qfmy.common.utils.JwtUtil;
import com.qfmy.common.utils.QrCodeUtils;
import com.qfmy.model.entity.user.UserInfo;
import com.qfmy.model.enums.BaseStatus;
import com.qfmy.model.query.user.LoginQuery;
import com.qfmy.model.vo.user.LoginVo;
import com.qfmy.model.vo.user.UserInfoVo;
import com.qfmy.web.app.mapper.user.UserMapper;
import com.qfmy.web.app.service.user.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.awt.image.BufferedImage;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * @author 清风明月
 * @Package com.qfmy.web.app.service.impl.user
 * @date 2025/10/30
 * @description 用户服务类
 */
@Slf4j
@Service
public  class UserInfoServiceImpl implements UserInfoService {

    /**
     * 注入默认的头像
     */
    @Value("${default.avatar}")
    private String defaultAvatar;

    /**
     * 注入用户mapper
     */
    @Autowired
    private UserMapper userMapper;

    /**
     * 注入redisTemplate
     */
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 引入微信服务
     */
    @Autowired
    private WxMaService wxMaService;

    /**
     * 待删除的key的前缀
     */
    private static final String WAIT_DELETE_KEY_PREFIX = "wait_delete:";

    /**
     * 获取临时登录令牌
     * @return
     */
    @Override
    public String getTempLoginToken() {
        //随机生成 -999999 - 0 之间的随机数
        Long userId = new Random().nextLong(-999999, 0);
        log.info("生成临时登录令牌：{}", userId);
        //生成token
        return JwtUtil.createToken(userId, "");
    }

    /**
     * 获取用户信息
     * @return
     */
    @Override
    public UserInfoVo getUserInfo() {
        //获取用户id
        Long userId = getUserId();
        //创建用户信息
        UserInfoVo userInfoVo = new UserInfoVo();
        //判断用户 id是否小于0
        if (userId < 0){
          //设置默认值
            userInfoVo = SetDefault();
            return userInfoVo;
        }
        //查询用户信息
        UserInfo userInfo = userMapper.getUserInfo(userId);
        //判断用户信息是否为空
        if (userInfo != null){
            userInfoVo = SetDefault();
        }
        //进行对象拷贝
        BeanUtils.copyProperties(userInfo, userInfoVo);
        log.info("获取用户信息：{}", userInfoVo);
        return userInfoVo;
    }

    /**
     * 获取用户二维码
     * @return
     */
    @Override
    public String getUserQrCode() {
        //获取用户id
        Long userId = getUserId();
        //根据用户id查询信息
        UserInfo userInfo = userMapper.getUserInfo(userId);
        //创建redis的key
        String key = "user_qr_code" + userId;
        //获取redis中的二维码
        String qrCode = (String) redisTemplate.opsForValue().get(key);
        //判断是否存在
        if (!StringUtils.hasText(qrCode)) {
            // 构建二维码内容
            String qrCodeContent = "用户ID:" + userId + ",用户名:" + userInfo.getUserName();
            //获取用户头像
            String avatar = userInfo.getAvatar();
            try {
                BufferedImage bufferedImage = QrCodeUtils.generateQrCodeWithLogo(qrCodeContent, avatar, null);
                qrCode = QrCodeUtils. bufferedImageToBase64(bufferedImage, "png");
                log.info("生成二维码成功！{}", qrCode);
                //保存二维码到redis并且设置过期时间为10小时
                redisTemplate.opsForValue().set(key, qrCode,12, TimeUnit.HOURS);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return qrCode;
    }

    /**
     * 更新用户喜欢类型
     * @param categoryName
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserLikeType(String categoryName) {
        //获取用户id
        Long userId = getUserId();

        //判断用户id是否小于0
        if (userId > 0){
            //获取用户信息
            UserInfo userInfo = userMapper.getUserInfo(userId);
            //获取喜欢类型
            String like = userInfo.getLikeType();
            //判断喜欢类型是否为空
            if(!StringUtils.hasText( like))
            {
                userInfo.setLikeType(categoryName);
                return;
            }
            String[] likeTypes = like.split(",");
            //循环判断是否存在
            for (String likeType : likeTypes) {
                if (likeType.equals(categoryName)){
                    return;
                }
                userInfo.setLikeType(userInfo.getLikeType() + "," + categoryName);
            }
            //更新用户信息
            userMapper.UpdateUserInfo(userInfo);
        }
    }

    /**
     * 充值vip
     * @param amount
     * @param day
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rechargeVip(double amount, Integer day) {
        //获取用户id
        Long userId = getUserId();
        if (userId > 0){
            UserInfo userInfo = userMapper.getUserInfo(userId);
            //获取余额
            BigDecimal balance = userInfo.getBalance();
            //判断余额是否充足
            if (balance.compareTo(BigDecimal.valueOf(amount)) >= 0){
                //进行充值
                userInfo.setBalance(balance.subtract(BigDecimal.valueOf(amount)));
                //根据充值的余额获取vip等级
                Integer vipLevel = getVipLevel(BigDecimal.valueOf(amount));
                userInfo.setVipLevel(vipLevel);
                userInfo.setVipExpired(LocalDateTime.now().plusDays(day));
                //更新用户信息
                userMapper.UpdateUserInfo(userInfo);
            }

        }
    }

    /**
     * 修改用户头像
     * @param avatar
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserAvatar(String avatar) {
        //获取用户id
        Long userId = getUserId();
        if (userId > 0){
            UserInfo userInfo = userMapper.getUserInfo(userId);
            userInfo.setAvatar(avatar);
            userMapper.UpdateUserInfo(userInfo);
            //创建redis的key
            String key = "user_qr_code" + userId;
            //删除原来的二维码
            redisTemplate.delete(key);
        }
    }

    /**
     * 修改用户信息
     * @param userInfo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserInfo(UserInfo userInfo) {
        //获取用户id
        Long userId = getUserId();
        userMapper.UpdateUserInfo(userInfo);
        //创建redis的key
        String key = "user_qr_code" + userId;
        //删除原来的二维码
        redisTemplate.delete(key);
    }

    /**
     * 微信登入
     * @param
     * @return
     */
    @Override
    public LoginVo WxLogin(LoginQuery loginQuery) {
        try {
            //校验登录参数
            Assert.notNull(loginQuery, "登录参数不能为空");
            //获取code
            String code = loginQuery.getCode();

            //根据code向微信服务器请求用户信息
            WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);

            //获取用户的openid
            String openid = sessionInfo.getOpenid();

            //根据openid获取用户信息
            UserInfo userInfo = userMapper.getUserInfoByOpenId(openid);

            //判断用户是否存在
            if (userInfo == null){
                userInfo = new UserInfo();
                //创建用户信息
                if (SetDefault() != null) {
                    BeanUtils.copyProperties(SetDefault(), userInfo);
                }
                //设置openid
                userInfo.setWxOpenId(openid);
                //设置手机号
                userInfo.setPhone(loginQuery.getPhone());
                //设置设备id
                userInfo.setDeviceId(loginQuery.getDeviceId());
                //设置设备类型
                userInfo.setDeviceType(loginQuery.getDeviceType());
                //设置最后登录时间
                userInfo.setLastLoginTime(LocalDateTime.now());
                //插入用户信息
                userMapper.InsertUserInfo(userInfo);
            }

            //校验用户的状态
            checkAndUpdateUserStatus(userInfo);

            //生成token
            String token = JwtUtil.createToken(userInfo.getUserId(),loginQuery.getPhone());

            //创建用户信息
            UserInfoVo userInfoVo = new UserInfoVo();
            //拷贝用户信息
            BeanUtils.copyProperties(userInfo, userInfoVo);

            //创建登录信息
            return new LoginVo(token,userInfoVo);

        } catch (Exception e) {
            log.error("微信登入异常",e);
            throw new BaseException(ResultCodeEnum.SERVICE_ERROR);
        }
    }

    /**
     * 手机号登入
     * @param loginQuery
     * @return
     */
    @Override
    public LoginVo phoneLogin(LoginQuery loginQuery) {
        //校验参数
        checkParams(loginQuery);

        //获取用户信息
        UserInfo userInfo = userMapper.getUserInfoByPhone(loginQuery.getPhone());

        //判断用户是否存在
        if (userInfo == null){
            userInfo = new UserInfo();
            //创建用户信息
            if (SetDefault() != null) {
                BeanUtils.copyProperties(SetDefault(), userInfo);
            }
            //设置手机号
            userInfo.setPhone(loginQuery.getPhone());
            //设置设备id
            userInfo.setDeviceId(loginQuery.getDeviceId());
            //设置设备类型
            userInfo.setDeviceType(loginQuery.getDeviceType());
            //设置最后登录时间
            userInfo.setLastLoginTime(LocalDateTime.now());
            //插入用户信息
            userMapper.InsertUserInfo(userInfo);
        }else{
            //校验用户的状态
            checkAndUpdateUserStatus(userInfo);
            //更新用户信息
            userInfo.setLastLoginTime(LocalDateTime.now());
            userMapper.UpdateUserInfo(userInfo);
        }

        //生成token
        String token = JwtUtil.createToken(userInfo.getUserId(),loginQuery.getPhone());

        //创建用户信息
        UserInfoVo userInfoVo = new UserInfoVo();
        //拷贝用户信息
        BeanUtils.copyProperties(userInfo, userInfoVo);

        //创建登录信息
        return new LoginVo(token,userInfoVo);
    }

    /**
     * 登出
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void logout() {
        //获取用户id
        Long userId = getUserId();
        //创建redis的key
        String key = "wait_delete:" + userId;
        //将要注销的用户id存放到redis中
        redisTemplate.opsForValue().set(key, String.valueOf(userId),3,TimeUnit.DAYS);
        //根据用户id查询用户信息
        UserInfo user = userMapper.getUserInfo(userId);
        //更新用户状态
        user.setStatus(BaseStatus.WAIT_DELETE.getCode());
        //更新用户信息
        userMapper.UpdateUserInfo(user);
    }

    /**
     * 获取今日阅读时长
     * @return
     */
    @Override
    public BigDecimal getTodayReadTime() {
        //获取用户id
        Long userId = getUserId();
        //如果用户id < 0
        if (userId < 0) {
            return new BigDecimal("0.0");
        }
        //创建缓存key
        String key = "today_read_time:" + userId;
        //从缓存中获取数据
        String value = (String) redisTemplate.opsForValue().get(key);
        return new BigDecimal(Objects.requireNonNullElse(value, "0.0"));
    }

    /**
     * 更新今日阅读时长
     * @param readTime
     * @param type
     */
    @Override
    public void updateTodayReadTime(Integer readTime, Integer type) {
        //获取用户id
        Long userId = getUserId();
        //如果用户id < 0
        if (userId < 0) {
            return;
        }
        //创建缓存key
        String key = "today_read_time:" + userId;
        //从缓存中获取数据
        String value = (String) redisTemplate.opsForValue().get(key);
        BigDecimal todayReadTime = new BigDecimal(Objects.requireNonNullElse(value, "0.0"));
        todayReadTime = todayReadTime.add(new BigDecimal(readTime));
        redisTemplate.opsForValue().set(key, todayReadTime.toString(), 24, TimeUnit.DAYS);
        //判断时听书还是阅读
        if(type == 0)
        {
            userMapper.updateTodayReadTime(todayReadTime, userId);
        }else{
            userMapper.updateTodayListenTime(todayReadTime, userId);
        }
    }

    /**
     * 获取图形验证码
     * @return
     */
    @Override
    public CodeResult getGraphicCode() {
        // 生成默认配置的验证码
        GraphicCodeResult result = GraphicCodeUtil.generate();

        // 1. 验证码文本（需保存到Session/Redis，用于后续校验用户输入）
        String code = result.getCode().toLowerCase();

        // 2. 图片Base64（返回给前端，用于<img>标签展示）
        String imageBase64 = result.getImageBase64();
        //创建缓存键
        String key = "graphic_code:" + code;
        //将验证码保存到Redis中
        redisTemplate.opsForValue().set(key, code, RedisConstant.APP_GRAPHIC_CODE_TTL, TimeUnit.MINUTES);
        //创建结果对象
        CodeResult codeResult = new CodeResult();
        codeResult.setKey(key);
        codeResult.setQrCode(imageBase64);
        return codeResult;
    }

    /**
     * 手机号加验证码登入
     *
     * @param loginQuery
     * @return
     */
    @Override
    public LoginVo login(@NotNull LoginQuery loginQuery) {
        //获取参数
        String phone = loginQuery.getPhone();
        String code = loginQuery.getCaptcha();
        String key = loginQuery.getKey();
        //校验参数
        cheackParams(phone,code,key);
        //根据key判断缓存中是否存在
        Boolean b = redisTemplate.hasKey(key);
        if (!b) {
            throw new BaseException(ResultCodeEnum.APP_LOGIN_CODE_EXPIRED);
        }
        //校验验证码
        String cs = Objects.requireNonNull(redisTemplate.opsForValue().get(key)).toString();
        //统一转成小写
        code = code.toLowerCase();
        if (!code.equals(cs)) {
            throw new BaseException(ResultCodeEnum.APP_LOGIN_CODE_ERROR);
        }
        //将key从缓存中删除
        redisTemplate.delete(key);
        //根据手机号获取用户信息
        UserInfo userInfo = userMapper.getUserInfoByPhone(phone);
        if (userInfo == null) {
            //进行注册
            userInfo = new UserInfo();
            //创建用户信息
            if (SetDefault() != null) {
                BeanUtils.copyProperties(SetDefault(), userInfo);
            }
            //设置手机号
            userInfo.setPhone(phone);
            //设置设备id
            userInfo.setDeviceId(loginQuery.getDeviceId());
            //设置设备类型
            userInfo.setDeviceType(loginQuery.getDeviceType());
            //设置最后登录时间
            userInfo.setLastLoginTime(LocalDateTime.now());
            //插入用户信息
            userMapper.InsertUserInfo(userInfo);
        }else{
            //校验用户的状态
            checkAndUpdateUserStatus(userInfo);
            userInfo.setLastLoginTime(LocalDateTime.now());
            userMapper.UpdateUserInfo(userInfo);
        }

        //生成token
        String token = JwtUtil.createToken(userInfo.getUserId(),loginQuery.getPhone());

        //创建用户信息
        UserInfoVo userInfoVo = new UserInfoVo();
        //拷贝用户信息
        BeanUtils.copyProperties(userInfo, userInfoVo);

        //创建登录信息
        return new LoginVo(token,userInfoVo);
    }



    /**
     * 校验参数
     */
    private void checkParams(LoginQuery loginQuery) {
        //校验参数
        if (loginQuery == null) {
            throw new BaseException(ResultCodeEnum.PARAM_ERROR);
        }

        //获取手机号
        String phone = loginQuery.getPhone();

        // 更精确的手机号格式校验
        if (!phone.matches("^1(3[0-9]|4[579]|5[0-35-9]|6[56]|7[0135678]|8[0-9]|9[1389])\\d{8}$")) {
            throw new BaseException(ResultCodeEnum.APP_LOGIN_PHONE_ERROR);
        }
    }

    /**
     * 检查并更新老用户状态（抽取为私有方法）
     */
    private void checkAndUpdateUserStatus(@NotNull UserInfo user) {
        // 检查是否被禁用
        if (BaseStatus.DISABLE.getCode().equals(user.getStatus())) {
            throw new BaseException(ResultCodeEnum.APP_ACCOUNT_DISABLED_ERROR);
        }

        // 检查是否待注销，若在Redis中则恢复启用
        if (BaseStatus.WAIT_DELETE.getCode().equals(user.getStatus())) {
            String redisKey = WAIT_DELETE_KEY_PREFIX + user.getUserId();
            try {
                if (Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
                    user.setStatus(BaseStatus.ENABLE.getCode());
                    log.info("用户{}从待注销状态恢复启用", user.getUserId());
                }
            } catch (Exception e) {
                log.warn("Redis检查待注销状态失败，userId: {}", user.getUserId(), e);
            }
        }

        // 更新最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        int update = userMapper.UpdateUserInfo(user);
        if (update <= 0) {
            log.warn("更新用户登录时间失败，userId: {}", user.getUserId());
        }
    }

    /**
     *
     * @param balance
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private Integer getVipLevel(@NotNull BigDecimal balance) {
        //青铜
       if(balance.compareTo(BigDecimal.valueOf(10)) <= 0)
       {
           return 1;
       }
       //黄金
       if(balance.compareTo(BigDecimal.valueOf(50)) <= 0 && balance.compareTo(BigDecimal.valueOf(10)) > 0)
       {
           return 2;
       }
       //钻石
       return 3;
    }

    /**
     * 校验参数
     * @param phone
     * @param code
     * @param key
     */
    private void cheackParams(String phone, String code, String key) {
        //判断参数是否为空
        if (!StringUtils.hasText(phone) || !StringUtils.hasText(code) || !StringUtils.hasText(key)) {
            throw new BaseException(ResultCodeEnum.PARAM_ERROR);
        }
        // 更精确的手机号格式校验
        if (!phone.matches("^1(3[0-9]|4[579]|5[0-35-9]|6[56]|7[0135678]|8[0-9]|9[1389])\\d{8}$")) {
            throw new BaseException(ResultCodeEnum.APP_LOGIN_PHONE_ERROR);
        }
    }

    /**
     * 设置默认值
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private UserInfoVo SetDefault() {
        //创建用户信息
        UserInfoVo userInfoVo = new UserInfoVo();
        //设置默认值
        userInfoVo.setUserId(getUserId());
        userInfoVo.setUserName("用户" + new Random().nextInt(10000));
        userInfoVo.setAvatar(defaultAvatar);
        userInfoVo.setCoupon(0);
        userInfoVo.setBalance(BigDecimal.ZERO);
        userInfoVo.setFollowCount(0);
        userInfoVo.setLikeCount(0);
        userInfoVo.setReadTime(0);
        userInfoVo.setListenTime(0);
        userInfoVo.setIntroduction("这个人很懒，什么都没有留下");
        userInfoVo.setRole(0);
        userInfoVo.setVipLevel(0);
        userInfoVo.setGender(2);
        userInfoVo.setLikeType("");
        userInfoVo.setRegion("");
        return userInfoVo;
    }

    /**
     * 获取用户id
     */
    private Long getUserId(){
        LoginUser loginUser = LoginUserHolder.getLoginUser();
        //判断是否为空
        if (loginUser == null){
            return 0L;
        }
        //获取用户id
        return loginUser.getUserId();
    }


}
